package wangwenjun.phase3.executor.unchecked;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import util.ThreadUtil;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * @author ChangLiang
 * @date 2020/6/23
 */
@Slf4j
public class UncheckedExceptionTestV3 {

    @Test
    @DisplayName("test thread monitor pattern")
    public void test3() throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        IntStream.range(0, 10).boxed().forEach(i -> executorService.execute(new NotifierTask(
                new MyListener() {
                    @Override
                    public void onStart(Thread thread) {
                        log.warn("onStart");
                    }

                    @Override
                    public void onRunning(Thread thread) {

                    }

                    @Override
                    public void onFinish(Thread thread) {
                        log.info("taskId:{} finished", i);
                    }

                    @Override
                    public void onError(Thread thread, Throwable throwable) {
                        log.error("taskId:{} error", i);
                        log.error(ThreadUtil.exceptionToString(throwable));
                    }
                }
                , () -> {
            // 让 10 threads全部启动 防止线程被复用
            ThreadUtil.sleepForSeconds(2);
            if (i % 2 == 0) {
                int j = 1 / 0;
            } else {
                log.info(" i = " + i);
            }
        })));
        executorService.shutdown();
        executorService.awaitTermination(10, TimeUnit.MINUTES);
        log.warn("test#finish");
    }

    interface MyListener {
        void onStart(Thread thread);

        void onRunning(Thread thread);

        void onFinish(Thread thread);

        void onError(Thread thread, Throwable throwable);
    }

    final class NotifierTask implements Runnable {

        private final MyListener listener;

        private final Runnable runnable;

        public NotifierTask(MyListener listener, Runnable runnable) {
            this.listener = listener;
            this.runnable = runnable;
        }

        private void notify(state state, Throwable throwable) {
            switch (state) {
                case STARTED:
                    listener.onStart(Thread.currentThread());
                    break;
                case RUNNING:
                    listener.onRunning(Thread.currentThread());
                    break;
                case FINISHED:
                    listener.onFinish(Thread.currentThread());
                    break;
                case ERROR:
                    listener.onError(Thread.currentThread(), throwable);
                    break;
                default:
                    break;
            }
        }

        @Override
        public void run() {
            notify(state.STARTED, null);
            try {
                this.notify(state.RUNNING, null);
                runnable.run();
                this.notify(state.FINISHED, null);
            } catch (Exception e) {
                this.notify(state.ERROR, e);
            }
        }
    }

    enum state {
        STARTED,
        RUNNING,
        FINISHED,
        ERROR
    }
}
